using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for ComponentVerilog.
	/// </summary>
	public class ComponentVerilog
	{
		const string VERILOG_EXTENTION = ".v";
		System.IO.TextWriter oTextWriter;
		
		public string VerilogModuleName;
		public string VerilogText;

		Component oComponent;

        public Component ParentComponent
        {
            get { return oComponent; }
        }

		
		public ComponentVerilog(Component poComponent)
		{
			oComponent = poComponent;
			VerilogText = "";
			VerilogModuleName = oComponent.ComponentTypeId; 
		}

		private void WriteVerilogHeader()
		{
			string sText;
			int iPortCount;
			int iTotalPorts;

			VerilogTraceLog("Writing Verilog Header");

			// first line
			sText = "module " + VerilogModuleName;
			this.WriteVerilogText(sText);

			// input port headings
			iPortCount = 0;
			iTotalPorts = oComponent.InputNodeVectors.Count  + oComponent.OutputNodeVectors.Count;
			foreach(NodeVector oNodeVector in oComponent.InputNodeVectors)
			{
				VerilogTraceLog("Writing Input Port",oNodeVector);

				sText = oNodeVector.Name;

				iPortCount++;
				if(iPortCount == 1)
					sText = "(" + sText;
				if(iPortCount < iTotalPorts)
					sText += ",";
				else
					sText += ");";

				WriteVerilogText(sText,1);			
			}

			// output port headings
			foreach(NodeVector oNodeVector in oComponent.OutputNodeVectors)
			{
				VerilogTraceLog("Writing Output Port",oNodeVector);

				sText = oNodeVector.Name;

				iPortCount++;
				if(iPortCount == 1)
					sText = "(" + sText;
				if(iPortCount < iTotalPorts)
					sText += ",";
				else
					sText += ");";

				WriteVerilogText(sText,1);			
			}

			WriteVerilogSkip();		

			// declare detailed input port headings
			foreach(NodeVector oNodeVector in oComponent.InputNodeVectors)
			{
				sText = "input " + this.NodeVectorAsVerilogBitVectorDeclaration(oNodeVector) + ";"; 
				WriteVerilogText(sText,2);			
			}

			WriteVerilogSkip();		

			// declare detailed output port headings
			foreach(NodeVector oNodeVector in oComponent.OutputNodeVectors)
			{
				sText = "output " + this.NodeVectorAsVerilogBitVectorDeclaration(oNodeVector) + ";"; 
				WriteVerilogText(sText,2);			
			}

			WriteVerilogSkip();		

			// declare detailed wire declarations
			foreach(NodeVector oNodeVector in oComponent.ConnectionOnlyNodeVectors)
			{
				sText = "wire " + this.NodeVectorAsVerilogBitVectorDeclaration(oNodeVector) + ";"; 
				WriteVerilogText(sText,2);			
			}

			WriteVerilogSkip();		

			// declare redundant wire declarations
			foreach(NodeVector oNodeVector in oComponent.RedundantNodeVectors)
			{
				sText = "wire " + this.NodeVectorAsVerilogBitVectorDeclaration(oNodeVector) + ";"; 
				WriteVerilogText(sText,2);			
			}

			WriteVerilogSkip();		
		}

		private void WriteVerilogFooter()
		{
			WriteVerilogSkip();	
			WriteVerilogText("endmodule");
		}


		private void WriteVerilogChildComponentDeclarations()
		{
			WriteVerilogSkip();
			VerilogTraceLog("Writing Verilog Wiring");

			WriteVerilogHeadingComment("Redundant Wiring");
			WriteVerilogNetDeclarations();			

			WriteVerilogHeadingComment("Subcomponet Declarations");
			foreach(Component oChildComponent in oComponent.Components.Values)
			{
				WriteVerilogSkip();		
				VerilogTraceLog("Getting child component declaration for {" + oComponent.HirarchicalName + "}");

				oChildComponent.ComponentVerilog.WriteVerilogComponentDeclaration(this);
			}
			WriteVerilogSkip();
		}

		protected void WriteVerilogComponentDeclaration(ComponentVerilog poComponentVerilog)
		{
			int iPortCount;
			int iTotalPorts;
			string sComma = "";
			string sPortMap = "";

            if (oComponent.OutputNodeVectors.Count == 0)
            {
                poComponentVerilog.WriteVerilogComment("Skipping  " + VerilogModuleName + " with 0 outputs.");
                return;		
            }

			VerilogTraceLog("Writing Component Declaration For Parent Component");

			poComponentVerilog.WriteVerilogText(VerilogModuleName + " " + oComponent.Name + " (");
				
			// input port headings
			iPortCount = 0;
			iTotalPorts = oComponent.InputNodeVectors.Count + oComponent.OutputNodeVectors.Count;
			foreach(NodeVector oNodeVector in oComponent.InputNodeVectors)
			{
				iPortCount++;
				if(iPortCount < iTotalPorts)
					sComma = ",";
				else
					sComma = "";

				sPortMap = VerilogNodeVectorCriticalPortMap(oNodeVector,oComponent.ParentComponent);
				poComponentVerilog.WriteVerilogText("." + oNodeVector.Name + "(" + sPortMap + ")" + sComma,2);

				VerilogTraceLog("Parent Input Port Map: '" + sPortMap + "'",oNodeVector);

			}

			// output port headings
			foreach(NodeVector oNodeVector in oComponent.OutputNodeVectors)
			{
				iPortCount++;
				if(iPortCount < iTotalPorts)
					sComma = ",";
				else
					sComma = "";

				sPortMap = VerilogNodeVectorCriticalPortMap(oNodeVector,oComponent.ParentComponent);
				poComponentVerilog.WriteVerilogText("." + oNodeVector.Name + "(" + sPortMap + ")" + sComma,2);
				
				VerilogTraceLog("Parent Output Port Map: '" + sPortMap + "'",oNodeVector);
			}
			poComponentVerilog.WriteVerilogText(");",1);
		}

		
		public bool TransformStructureToVerilog()
		{
            if (oComponent.OutputNodeVectors.Count == 0)
                return true;

			VerilogTraceLog("************* Opening File For Verilog Export *****************");
			VerilogText = "";		
			OpenVerilogFile();
			WriteVerilogHeader();
			WriteVerilogChildComponentDeclarations();
			VerilogTraceLog("-------- Start Inherited Verilog Transform Function ----------------");
			oComponent.TransformStructureToVerilog();
			VerilogTraceLog("-------- End Inherited Verilog Transform Function ----------------");
			WriteVerilogFooter();
			CloseVerilogFile();
			VerilogTraceLog("************* FINISHED File For Verilog Export *****************");
			return true;
		}

		public void OpenVerilogFile()
		{
			VerilogModuleName = oComponent.ComponentTypeId;
			string sPath = oComponent.RapidHardware.Settings.OutputPath + @"temp\";
			int iVersionCount = 1;

            sPath = oComponent.RapidHardware.Settings.OutputPath + @"temp\";
			while(TextFile.FileExists(sPath,VerilogModuleName + VERILOG_EXTENTION))
			{
				iVersionCount++;
				VerilogModuleName = oComponent.ComponentTypeId + iVersionCount.ToString();
			}

			VerilogTraceLog("Set Module Name to " + VerilogModuleName);
			oTextWriter = TextFile.OpenFileReWrite(sPath,VerilogModuleName + VERILOG_EXTENTION);
		}

        public void RenameVerilogFile(string psOldName, string psNewName)
        {
            System.IO.TextReader trIn;
            System.IO.TextWriter twOut;

            string sPath = oComponent.RapidHardware.Settings.OutputPath + @"temp\";
            
            trIn = TextFile.OpenFileRead(sPath,psOldName + VERILOG_EXTENTION);
            twOut = TextFile.OpenFileReWrite(sPath,psNewName + VERILOG_EXTENTION);
            twOut.WriteLine("module " + psNewName);
            trIn.ReadLine();            
            twOut.Write(trIn.ReadToEnd());
            trIn.Close();
            twOut.Close();

        }

		public void CloseVerilogFile()
		{
			string sTempName;
			int iVersionCount=0;
            string sPath = oComponent.RapidHardware.Settings.OutputPath + @"temp\";
            bool bSkip;

			//oTextWriter.Write(VerilogText);
			oTextWriter.Close();

            bSkip = false;

			VerilogTraceLog("Closed Text Written To '" + VerilogModuleName + VERILOG_EXTENTION + "'");

			sTempName = oComponent.ComponentTypeId;
			
            while(TextFile.FileExists(sPath,sTempName + VERILOG_EXTENTION) & sTempName != VerilogModuleName)
			{
				if (TextFile.CheckFilesMatch(sPath,sTempName + VERILOG_EXTENTION,VerilogModuleName + VERILOG_EXTENTION,2))
				{
					VerilogTraceLog("VerilogModule '" + VerilogModuleName + "' matches '" + sTempName + "' so reverting.");
                    //RenameVerilogFile(VerilogModuleName,sTempName)
					TextFile.DeleteFile(sPath,VerilogModuleName + VERILOG_EXTENTION);
					VerilogModuleName = sTempName;
                    bSkip = true;
					continue;
				}
				iVersionCount++;
				sTempName = oComponent.ComponentTypeId + iVersionCount.ToString();
			}
            if (!bSkip)
            {
                XilinxTools.AddXSTScript(VerilogModuleName + VERILOG_EXTENTION);
            }

			VerilogTraceLog("VerilogModule '" + VerilogModuleName + "' matches '" + sTempName + "' so reverting.");
		}

		public void WriteVerilogSkip()
		{
			WriteVerilogText("",0);
		}

		public void WriteVerilogComment(string psHeading)
		{
			WriteVerilogHeadingComment(psHeading,0);
		}

		public void WriteVerilogComment(string psHeading,int piIndent)
		{
			WriteVerilogText("// " + psHeading,piIndent);
		}

		public void WriteVerilogHeadingComment(string psHeading)
		{
			WriteVerilogHeadingComment(psHeading,0);
		}

		public void WriteVerilogHeadingComment(string psHeading,int piIndent)
		{
			WriteVerilogSkip();
			WriteVerilogText("//" + Utility.StringRepeat("*",5) + "  " + psHeading + "  " + Utility.StringRepeat("*",5),piIndent);
			WriteVerilogSkip();	
		}

		public void WriteVerilogText(string psText)
		{
			WriteVerilogText(psText,0);
		}

		public void WriteVerilogText(string psText,int piIndent)
		{
			string sIndent = "";
			if (piIndent > 0)
				sIndent = Utility.StringRepeat(" ",3 * piIndent);

			oTextWriter.WriteLine(sIndent + psText);

			//if (VerilogText.Length != 0)
			//	VerilogText = VerilogText + "\n" + sIndent + psText;
			//else
			//	VerilogText = psText;
		}

		public string NodeAsVerilogBitVector(NodeVector poNodeVector)
		{
			string sVector = poNodeVector.Name;
			int iMax;

			if(poNodeVector.Size == 1)
				return sVector;

			iMax = poNodeVector.Size - 1;
			sVector += "[" + iMax.ToString() + ":0]";
			return sVector;
		}

		public string NodeVectorAsVerilogBitVectorDeclaration(NodeVector poNodeVector)
		{
			string sVector = poNodeVector.Name;
			int iMax;

			if(poNodeVector.Size == 1)
				return sVector;

			iMax = poNodeVector.Size - 1;
			sVector = "[" + iMax.ToString() + ":0] " + sVector;
			return sVector;
		}

		public string VerilogNodeVectorCriticalPortMap(NodeVector poNodeVector, Component poComponent)
		{
			// called at the level of the child component
			// locate parent component node vectors mapped to this child component's node vector

			Node oPortNode;
			Component oParentComponent;

			int iNodeStartPosition=0;
			int iNodeEndPosition=0;
			int iMaxNodeEndPosition=0;
			
			int iPortCount=0;
			
			string sNodeVector="";
			string sPort;
			string sPortMap="";
			

			int iNodeIndex = 1;

			oParentComponent = poNodeVector.ParentComponent;

			System.Collections.ArrayList oPortNodes = new System.Collections.ArrayList(poNodeVector.Size);

			// need to figure out which nodes in the net are getting connected in the port map
			foreach(Node oNode in poNodeVector.Nodes)
			{
				oPortNode = oNode.Net.SelectCriticalNode(poComponent);
				if (oPortNode == null)
					System.Diagnostics.Debug.Fail("Verilog error in structure of " + oNode.HirarchicalName + " because no critical node found to connect port.");

				oPortNodes.Add(oPortNode);
			}

			iNodeIndex = 0;
			iPortCount = 0;
			foreach(Node oNode in oPortNodes)
			{
				if (iNodeIndex == 0)
				{
					iNodeStartPosition = oNode.Position;
					iNodeEndPosition = iNodeStartPosition;
					iMaxNodeEndPosition = oNode.ParentNodeVector.Size - 1;
					sNodeVector = oNode.ParentNodeVector.Name;
				}
				else
				{
					if(iNodeEndPosition + 1 != oNode.Position | oNode.ParentNodeVector.Name != sNodeVector)
					{
						sPort = sNodeVector;
						if (iNodeStartPosition != 0 | iNodeEndPosition != iMaxNodeEndPosition)
						{
							if (iNodeStartPosition == iNodeEndPosition)
								sPort += "[" + iNodeStartPosition.ToString() + "]";
							else
								sPort += "[" + iNodeEndPosition.ToString() + ":" + iNodeStartPosition.ToString() + "]";

						}
						iNodeStartPosition = oNode.Position;
						iNodeEndPosition = iNodeStartPosition;
						iMaxNodeEndPosition = oNode.ParentNodeVector.Size - 1;
						sNodeVector = oNode.ParentNodeVector.Name;
					
						if (iPortCount == 0)
							sPortMap = sPort + "}";
						else
							sPortMap = sPort + "," + sPortMap;
					
						iPortCount++;
					}
					else
						iNodeEndPosition++;
				}
				iNodeIndex++;
			}
			
			sPort = sNodeVector;
			if (iNodeStartPosition != 0 | iNodeEndPosition != iMaxNodeEndPosition)
			{
				if (iNodeStartPosition == iNodeEndPosition)
					sPort += "[" + iNodeStartPosition.ToString() + "]";
				else
					sPort += "[" + iNodeEndPosition.ToString() + ":" + iNodeStartPosition.ToString() + "]";

			}
			if (iPortCount == 0)
				sPortMap = sPort + "}";
			else
				sPortMap = sPort + "," + sPortMap;

			return "{" + sPortMap;
		}

		public void WriteVerilogNetDeclarations()
		{
			foreach(NodeVector oNodeVector in oComponent.RedundantNodeVectors)
			{
				WriteVerilogText("assign " +  oNodeVector.Name + " = " + VerilogNodeVectorCriticalPortMap(oNodeVector,oComponent) + ";",3);
			}
		}


		public void VerilogTraceLog(string psLogEntry)
		{
			VerilogTraceLog(psLogEntry,null,null);
		}

		public void VerilogTraceLog(string psLogEntry, NodeVector poNodeVector)
		{
			VerilogTraceLog(psLogEntry,poNodeVector,null);
		}

		public void VerilogTraceLog(string psLogEntry, Node poNode)
		{
			VerilogTraceLog(psLogEntry,null,poNode);
		}

		public void VerilogTraceLog(string psLogEntry, NodeVector poNodeVector, Node poNode)
		{
            // disabled for now
            return;
			string sLogEntry = psLogEntry + " - Component Verilog Trace: {" + oComponent.HirarchicalName + "} ";
			if (poNodeVector != null)
			{
				sLogEntry += "\n       " + poNodeVector.GetLogIdString(LogType.VerilogTrace);
			}
			if (poNode != null)
			{
				sLogEntry += "\n       " + poNode.GetLogIdString(LogType.VerilogTrace);
			}
			oComponent.RapidHardware.InternalLog.Log(LogType.VerilogTrace,sLogEntry);
		}

        public string FileName
        {
            get
            {
                return oComponent.RapidHardware.Settings.OutputPath + this.VerilogModuleName + VERILOG_EXTENTION;
            }
        }

        internal void RecurseDependencyList(ref System.Collections.Generic.List<string> slDependencyList)
        {
            foreach (Component oChild in oComponent.Components.Values)
            {
                oChild.ComponentVerilog.RecurseDependencyList(ref slDependencyList);
            }
            if (!slDependencyList.Contains(VerilogModuleName) && VerilogModuleName != null)
                slDependencyList.Add(VerilogModuleName);
        }

        public System.Collections.Generic.List<string> GetDependencyList()
        {
            System.Collections.Generic.List<string> slList = new System.Collections.Generic.List<string>();
            RecurseDependencyList(ref slList);
            return slList;
        }
	}
}
